package com.sds.service.impl;

import com.sds.service.LockService;
import org.springframework.stereotype.Service;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Service
public class LockServiceImpl implements LockService {
    // 可重入锁
    private final ReentrantLock reentrantLock = new ReentrantLock();
    // 非可重入锁
    private final NonReentrantLock unReentrantLock = new NonReentrantLock();
    // 乐观锁
    private final AtomicInteger optimisticLock = new AtomicInteger(0);
    // 悲观锁
    private final ReentrantLock pessimisticLock = new ReentrantLock();
    // 公平锁
    private final ReentrantLock fairLock = new ReentrantLock(true);
    // 非公平锁
    private final ReentrantLock unfairLock = new ReentrantLock(false);
    // 互斥锁
    private final ReentrantLock exclusiveLock = new ReentrantLock();
    // 共享锁
    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();


    /**
     * 使用可重入锁的临界区代码
     */
    private void criticalSectionWithReentrantLock() {
        reentrantLock.lock(); // 尝试获取锁
        try {
            System.out.println(Thread.currentThread().getName() + " 正在执行临界区代码");
        } finally {
            reentrantLock.unlock(); // 释放锁
        }
    }

    /**
     * 使用非可重入锁的临界区代码
     */
    private void criticalSectionWithNonReentrantLock() {
        unReentrantLock.lock(); // 尝试获取锁
        try {
            System.out.println(Thread.currentThread().getName() + " 正在执行临界区代码");
        } finally {
            unReentrantLock.unlock(); // 释放锁
        }
    }

    /**
     * 1、可重入锁
     */
    @Override
    public void reentrantLock() {
        Thread t1 = new Thread(() -> {
            reentrantLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                criticalSectionWithReentrantLock(); // 进入临界区
                criticalSectionWithReentrantLock(); // 再次进入临界区
            } finally {
                reentrantLock.unlock();
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
            }
        });

        Thread t2 = new Thread(() -> {
            reentrantLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                criticalSectionWithReentrantLock(); // 进入临界区
                criticalSectionWithReentrantLock(); // 再次进入临界区
            } finally {
                reentrantLock.unlock();
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
            }
        });

        t1.start();
        t2.start();
    }

    /**
     * 2、非可重入锁
     */
    @Override
    public void nonReentrantLock() {
        Thread t1 = new Thread(() -> {
            unReentrantLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                criticalSectionWithNonReentrantLock(); // 进入临界区
                criticalSectionWithNonReentrantLock(); // 再次进入临界区
            } finally {
                unReentrantLock.unlock();
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
            }
        });

        t1.start();
    }

    /**
     * 3、乐观锁
     */
    @Override
    public void optimisticLock() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            int currentVersion = optimisticLock.get(); // 获取当前版本号
            System.out.println(Thread.currentThread().getName() + " 获取当前版本号：" + currentVersion);
            // 模拟对共享资源的读操作
            try {
                Thread.sleep(1000); // 假设读操作耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 乐观地尝试更新版本号
            if (optimisticLock.compareAndSet(currentVersion, currentVersion + 1)) {
                System.out.println(Thread.currentThread().getName() + " 更新版本号成功，新版本号：" + (currentVersion + 1));
            } else {
                System.out.println(Thread.currentThread().getName() + " 更新版本号失败，版本号已经被其他线程更新");
            }
        });
        Thread t2 = new Thread(() -> {
            int currentVersion = optimisticLock.get(); // 获取当前版本号
            System.out.println(Thread.currentThread().getName() + " 获取当前版本号：" + currentVersion);
            // 模拟对共享资源的读操作
            try {
                Thread.sleep(1000); // 假设读操作耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 乐观地尝试更新版本号
            if (optimisticLock.compareAndSet(currentVersion, currentVersion + 1)) {
                System.out.println(Thread.currentThread().getName() + " 更新版本号成功，新版本号：" + (currentVersion + 1));
            } else {
                System.out.println(Thread.currentThread().getName() + " 更新版本号失败，版本号已经被其他线程更新");
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();
    }

    /**
     * 4、悲观锁
     * 当一个线程试图获取一个内部的对象锁（也就是进入一个synchronized块）时，如果该锁被其他线程持有，则该线程将进入BLOCKED状态。
     * 但是，当一个线程等待一个Lock的释放时，它的状态是WAITING或TIMED_WAITING，而不是BLOCKED
     * 这是因为Lock是一个接口，它的实现可能不是使用内部的对象锁。
     */
    @Override
    public void pessimisticLock() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            pessimisticLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(1000); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
                pessimisticLock.unlock();
            }
        });
        Thread t2 = new Thread(() -> {
            // 线程2尝试获取锁，但是线程1已经持有锁，因此线程2会等待
            pessimisticLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(1000); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                pessimisticLock.unlock();
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();
    }

    /**
     * 5、公平锁
     */
    @Override
    public void fairLock() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            fairLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(1000); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
                fairLock.unlock();
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            fairLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(100); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
                fairLock.unlock();
            }
        }, "t2");
        Thread t3 = new Thread(() -> {
            fairLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(100); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
                fairLock.unlock();
            }
        }, "t3");

        t1.start();
        Thread.sleep(10);
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
    }

    /**
     * 6、非公平锁
     */
    @Override
    public void unfairLock() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            unfairLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(1000); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
                unfairLock.unlock();
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            unfairLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(100); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
                unfairLock.unlock();
            }
        }, "t2");
        Thread t3 = new Thread(() -> {
            unfairLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(100); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
                unfairLock.unlock();
            }
        }, "t3");

        t1.start();
        Thread.sleep(10);
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
    }

    /**
     * 互斥锁
     */
    @Override
    public void exclusiveLock() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            exclusiveLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(1000); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
                exclusiveLock.unlock();
            }
        });
        Thread t2 = new Thread(() -> {
            exclusiveLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了锁");
                Thread.sleep(100); // 模拟对共享资源的写操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了锁");
                exclusiveLock.unlock();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();
    }

    /**
     * 共享锁
     * 读线程在读取共享资源时获取读锁，可以允许多个读线程同时读取。
     * 写线程在修改共享资源时获取写锁，当写线程获取写锁时，不允许其他任何读线程和写线程访问共享资源
     */
    @Override
    public void sharedLock() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            readWriteLock.readLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了读锁");
                // 模拟对共享资源的读操作
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了读锁");
                readWriteLock.readLock().unlock();
            }
        });
        Thread t2 = new Thread(() -> {
            readWriteLock.readLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了读锁");
                // 模拟对共享资源的读操作
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了读锁");
                readWriteLock.readLock().unlock();
            }
        });
        Thread t3 = new Thread(() -> {
            readWriteLock.writeLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 已经获得了写锁");
                // 模拟对共享资源的写操作
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + " 已经释放了写锁");
                readWriteLock.writeLock().unlock();
            }
        });

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
    }

    /**
     * 实现一个非可重入锁
     */
    public class NonReentrantLock extends ReentrantLock {
        private Thread lockedBy = null;

        @Override
        public synchronized void lock() {
            // 如果锁已经被当前线程持有，则抛出异常
            if (isLocked()) {
                throw new IllegalMonitorStateException("Lock already held by another thread");
            }
            super.lock();
            lockedBy = Thread.currentThread();
        }

        @Override
        public synchronized void unlock() {
            // 只有持有锁的线程才能释放锁
            if (lockedBy == Thread.currentThread()) {
                super.unlock();
                lockedBy = null;
            }
        }
    }
}